﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Runtime.Remoting;
using System.Threading;

namespace WcfCore
{
	[Serializable]
	public class RemotingHostInitializer
	{
		public RemotingHostInitializer(WcfTestInput input)
		{
			_input = input;
			AppDomain.CurrentDomain.DomainUnload += new EventHandler(this.DomainUnload);
		}

		private readonly WcfTestInput _input;
		public WcfTestInput Input
		{
			get
			{
				return _input;
			}
		}

		private ServiceHost _wcfHost;
		public ServiceHost WcfHost
		{
			get
			{
				return _wcfHost;
			}
		}

		public void Initialize()
		{
			try
			{
				_wcfHost = WcfServiceHostFactory.CreateHost(this.Input);
			}
			catch (Exception exc)
			{
				WcfLogger.LogException(exc);
				throw;
			}
		}

		public void DomainUnload(object sender, EventArgs e)
		{
			if (this.WcfHost != null)
			{
				this.WcfHost.Close();
			}
		}
	}

	public class WcfServiceHostWrapper : IDisposable
	{
		public WcfServiceHostWrapper(WcfTestInput input)
		{
			switch (input.Scope)
			{
				case PerformanceTestScope.SameAppDomain:
					_wcfHost = WcfServiceHostFactory.CreateHost(input);
					break;
				case PerformanceTestScope.SameProcess:
					RemotingHostInitializer rhInitializer = new RemotingHostInitializer(input);
					_remotingHostDomain = AppDomain.CreateDomain("RemotingHostDomain");
					this.RemotingHostDomain.DoCallBack(rhInitializer.Initialize);
					break;
				default:
					throw new NotSupportedException("Scopes other than SameAppDomain or SameProcess aren't supported by WcfServiceHostWrapper.");
			}
		}

		private readonly AppDomain _remotingHostDomain;
		public AppDomain RemotingHostDomain
		{
			get
			{
				return _remotingHostDomain;
			}
		}

		private readonly ServiceHost _wcfHost;
		public ServiceHost WcfHost
		{
			get
			{
				return _wcfHost;
			}
		}

		#region IDisposable Members
		private bool _disposed;
		private void Dispose(bool disposing)
		{
			if (_disposed)
			{
				return;
			}

			if (disposing)
			{
				// Cleanup managed
				if (this.RemotingHostDomain != null)
				{
					int count = 0;
					bool unloaded = false;
					while ((count < 10) && (!unloaded))
					{
						try
						{
							AppDomain.Unload(this.RemotingHostDomain);
							unloaded = true;
						}
						catch (CannotUnloadAppDomainException exc)
						{
							Thread.Sleep(500);
						}
					}
				}

				if (this.WcfHost != null)
				{
					this.WcfHost.Close();
				}
			}

			// Cleanup unmanaged

			_disposed = true;
		}

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		~WcfServiceHostWrapper()
		{
			Dispose(false);
		}
		#endregion
	}
}
